home *** CD-ROM | disk | FTP | other *** search
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- // <include src="../../assert.js">
-
- cr.exportPath('cr.ui');
-
- /**
- * Enum for type of hide. Delayed is used when called by clicking on a
- * checkable menu item.
- * @enum {number}
- */
- cr.ui.HideType = {
- INSTANT: 0,
- DELAYED: 1
- };
-
- cr.define('cr.ui', function() {
- /** @const */
- var Menu = cr.ui.Menu;
-
- /** @const */
- var HideType = cr.ui.HideType;
-
- /** @const */
- var positionPopupAroundElement = cr.ui.positionPopupAroundElement;
-
- /**
- * Creates a new menu button element.
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
- * @extends {HTMLButtonElement}
- * @implements {EventListener}
- */
- var MenuButton = cr.ui.define('button');
-
- MenuButton.prototype = {
- __proto__: HTMLButtonElement.prototype,
-
- /**
- * Initializes the menu button.
- */
- decorate: function() {
- this.addEventListener('mousedown', this);
- this.addEventListener('keydown', this);
-
- // Adding the 'custom-appearance' class prevents widgets.css from changing
- // the appearance of this element.
- this.classList.add('custom-appearance');
- this.classList.add('menu-button'); // For styles in menu_button.css.
-
- var menu;
- if ((menu = this.getAttribute('menu')))
- this.menu = menu;
-
- // An event tracker for events we only connect to while the menu is
- // displayed.
- this.showingEvents_ = new EventTracker();
-
- this.anchorType = cr.ui.AnchorType.BELOW;
- this.invertLeftRight = false;
- },
-
- /**
- * The menu associated with the menu button.
- * @type {cr.ui.Menu}
- */
- get menu() {
- return this.menu_;
- },
- set menu(menu) {
- if (typeof menu == 'string' && menu[0] == '#') {
- menu = assert(this.ownerDocument.getElementById(menu.slice(1)));
- cr.ui.decorate(menu, Menu);
- }
-
- this.menu_ = menu;
- if (menu) {
- if (menu.id)
- this.setAttribute('menu', '#' + menu.id);
- }
- },
-
- /**
- * Whether to show the menu on press of the Up or Down arrow keys.
- */
- respondToArrowKeys: true,
-
- /**
- * Handles event callbacks.
- * @param {Event} e The event object.
- */
- handleEvent: function(e) {
- if (!this.menu)
- return;
-
- switch (e.type) {
- case 'mousedown':
- if (e.currentTarget == this.ownerDocument) {
- if (e.target instanceof Node && !this.contains(e.target) &&
- !this.menu.contains(e.target)) {
- this.hideMenu();
- } else {
- e.preventDefault();
- }
- } else {
- if (this.isMenuShown()) {
- this.hideMenu();
- } else if (e.button == 0) { // Only show the menu when using left
- // mouse button.
- this.showMenu(false);
-
- // Prevent the button from stealing focus on mousedown.
- e.preventDefault();
- }
- }
-
- // Hide the focus ring on mouse click.
- this.classList.add('using-mouse');
- break;
- case 'keydown':
- this.handleKeyDown(e);
- // If the menu is visible we let it handle all the keyboard events.
- if (this.isMenuShown() && e.currentTarget == this.ownerDocument) {
- if (this.menu.handleKeyDown(e)) {
- e.preventDefault();
- e.stopPropagation();
- }
- }
-
- // Show the focus ring on keypress.
- this.classList.remove('using-mouse');
- break;
- case 'focus':
- if (e.target instanceof Node && !this.contains(e.target) &&
- !this.menu.contains(e.target)) {
- this.hideMenu();
- // Show the focus ring on focus - if it's come from a mouse event,
- // the focus ring will be hidden in the mousedown event handler,
- // executed after this.
- this.classList.remove('using-mouse');
- }
- break;
- case 'activate':
- var hideDelayed = e.target instanceof cr.ui.MenuItem &&
- e.target.checkable;
- this.hideMenu(hideDelayed ? HideType.DELAYED : HideType.INSTANT);
- break;
- case 'scroll':
- if (!(e.target == this.menu || this.menu.contains(e.target)))
- this.hideMenu();
- break;
- case 'popstate':
- case 'resize':
- this.hideMenu();
- break;
- }
- },
-
- /**
- * Shows the menu.
- * @param {boolean} shouldSetFocus Whether to set focus on the
- * selected menu item.
- */
- showMenu: function(shouldSetFocus) {
- this.hideMenu();
-
- this.menu.updateCommands(this);
-
- var event = document.createEvent('UIEvents');
- event.initUIEvent('menushow', true, true, window, null);
-
- if (!this.dispatchEvent(event))
- return;
-
- this.menu.hidden = false;
-
- this.setAttribute('menu-shown', '');
-
- // When the menu is shown we steal all keyboard events.
- var doc = this.ownerDocument;
- var win = doc.defaultView;
- this.showingEvents_.add(doc, 'keydown', this, true);
- this.showingEvents_.add(doc, 'mousedown', this, true);
- this.showingEvents_.add(doc, 'focus', this, true);
- this.showingEvents_.add(doc, 'scroll', this, true);
- this.showingEvents_.add(win, 'popstate', this);
- this.showingEvents_.add(win, 'resize', this);
- this.showingEvents_.add(this.menu, 'activate', this);
- this.positionMenu_();
-
- if (shouldSetFocus)
- this.menu.focusSelectedItem();
- },
-
- /**
- * Hides the menu. If your menu can go out of scope, make sure to call this
- * first.
- * @param {cr.ui.HideType=} opt_hideType Type of hide.
- * default: cr.ui.HideType.INSTANT.
- */
- hideMenu: function(opt_hideType) {
- if (!this.isMenuShown())
- return;
-
- this.removeAttribute('menu-shown');
- if (opt_hideType == HideType.DELAYED)
- this.menu.classList.add('hide-delayed');
- else
- this.menu.classList.remove('hide-delayed');
- this.menu.hidden = true;
-
- this.showingEvents_.removeAll();
- this.focus();
- },
-
- /**
- * Whether the menu is shown.
- */
- isMenuShown: function() {
- return this.hasAttribute('menu-shown');
- },
-
- /**
- * Positions the menu below the menu button. At this point we do not use any
- * advanced positioning logic to ensure the menu fits in the viewport.
- * @private
- */
- positionMenu_: function() {
- positionPopupAroundElement(this, this.menu, this.anchorType,
- this.invertLeftRight);
- },
-
- /**
- * Handles the keydown event for the menu button.
- */
- handleKeyDown: function(e) {
- switch (e.keyIdentifier) {
- case 'Down':
- case 'Up':
- if (!this.respondToArrowKeys)
- break;
- case 'Enter':
- case 'U+0020': // Space
- if (!this.isMenuShown())
- this.showMenu(true);
- e.preventDefault();
- break;
- case 'Esc':
- case 'U+001B': // Maybe this is remote desktop playing a prank?
- case 'U+0009': // Tab
- this.hideMenu();
- break;
- }
- }
- };
-
- /**
- * Helper for styling a menu button with a drop-down arrow indicator.
- * Creates a new 2D canvas context and draws a downward-facing arrow into it.
- * @param {string} canvasName The name of the canvas. The canvas can be
- * addressed from CSS using -webkit-canvas(<canvasName>).
- * @param {number} width The width of the canvas and the arrow.
- * @param {number} height The height of the canvas and the arrow.
- * @param {string} colorSpec The CSS color to use when drawing the arrow.
- */
- function createDropDownArrowCanvas(canvasName, width, height, colorSpec) {
- var ctx = document.getCSSCanvasContext('2d', canvasName, width, height);
- ctx.fillStyle = ctx.strokeStyle = colorSpec;
- ctx.beginPath();
- ctx.moveTo(0, 0);
- ctx.lineTo(width, 0);
- ctx.lineTo(height, height);
- ctx.closePath();
- ctx.fill();
- ctx.stroke();
- };
-
- /** @const */ var ARROW_WIDTH = 6;
- /** @const */ var ARROW_HEIGHT = 3;
-
- /**
- * Create the images used to style drop-down-style MenuButtons.
- * This should be called before creating any MenuButtons that will have the
- * CSS class 'drop-down'. If no colors are specified, defaults will be used.
- * @param {string=} opt_normalColor CSS color for the default button state.
- * @param {string=} opt_hoverColor CSS color for the hover button state.
- * @param {string=} opt_activeColor CSS color for the active button state.
- */
- MenuButton.createDropDownArrows = function(
- opt_normalColor, opt_hoverColor, opt_activeColor) {
- opt_normalColor = opt_normalColor || 'rgb(192, 195, 198)';
- opt_hoverColor = opt_hoverColor || 'rgb(48, 57, 66)';
- opt_activeColor = opt_activeColor || 'white';
-
- createDropDownArrowCanvas(
- 'drop-down-arrow', ARROW_WIDTH, ARROW_HEIGHT, opt_normalColor);
- createDropDownArrowCanvas(
- 'drop-down-arrow-hover', ARROW_WIDTH, ARROW_HEIGHT, opt_hoverColor);
- createDropDownArrowCanvas(
- 'drop-down-arrow-active', ARROW_WIDTH, ARROW_HEIGHT, opt_activeColor);
- };
-
- // Export
- return {
- MenuButton: MenuButton,
- };
- });
-